Passed
Push — master ( 3c69a0...6dbd21 )
by EMP
01:17
created

main.js ➔ addContact   F

Complexity

Conditions 15

Size

Total Lines 19
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 15
dl 0
loc 19
rs 2.9998
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like main.js ➔ addContact often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"use strict";
2
3
sodium.ready.then(function() {
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
4
5
const ae = new AllEars(function(ok) {
1 ignored issue
show
Bug introduced by
The variable AllEars seems to be never declared. If this is a global, consider adding a /** global: AllEars */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
6
	if (ok) {
7
		const greeting = localStorage.greeting;
1 ignored issue
show
Bug introduced by
The variable localStorage seems to be never declared. If this is a global, consider adding a /** global: localStorage */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
8
		if (greeting) {
9
			document.getElementById("greeting").textContent = greeting;
10
			document.getElementById("txt_pg").value = greeting;
11
		}
12
13
		document.getElementById("txt_skey").style.background = "#466";
14
		document.getElementById("txt_skey").maxLength = "64";
15
	} else {
16
		console.log("Failed to load All-Ears");
17
	}
18
});
19
20
function TabState(cur, max, btnDele, btnUpdt) {
21
	this.cur = cur;
22
	this.max = max;
23
	this.btnDele = btnDele;
24
	this.btnUpdt = btnUpdt;
25
}
26
27
const tabs = [
28
	new TabState(0, 0, false, true), // Inbox
29
	new TabState(0, 0, false, true), // Outbx
30
	new TabState(0, 1, true, false), // Write
31
	new TabState(0, 2, false, false), // Notes
32
	new TabState(0, 3, false, true) // Tools
33
];
34
35
let showHeaders = false;
36
37
let tab = 0;
38
const TAB_INBOX = 0;
39
const TAB_DRBOX = 1;
40
const TAB_WRITE = 2;
41
const TAB_NOTES = 3;
42
const TAB_TOOLS = 4;
43
44
// Helper functions
45
function getCountryName(countryCode) {
46
	switch (countryCode) {
47
		case "DZ": return "Algeria";
48
		case "AO": return "Angola";
49
		case "BJ": return "Benin";
50
		case "BW": return "Botswana";
51
		case "BF": return "Burkina Faso";
52
		case "BI": return "Burundi";
53
		case "CV": return "Cabo Verde";
54
		case "CM": return "Cameroon";
55
		case "CF": return "Central African Republic";
56
		case "TD": return "Chad";
57
		case "KM": return "Comoros";
58
		case "CD": return "Congo";
59
		case "DJ": return "Djibouti";
60
		case "EG": return "Egypt";
61
		case "GQ": return "Equatorial Guinea";
62
		case "ER": return "Eritrea";
63
		case "SZ": return "Eswatini";
64
		case "ET": return "Ethiopia";
65
		case "GA": return "Gabon";
66
		case "GM": return "Gambia";
67
		case "GH": return "Ghana";
68
		case "GW": return "Guinea-Bissau";
69
		case "GN": return "Guinea";
70
		case "CI": return "Ivory Coast";
71
		case "KE": return "Kenya";
72
		case "LS": return "Lesotho";
73
		case "LR": return "Liberia";
74
		case "LY": return "Libya";
75
		case "MG": return "Madagascar";
76
		case "MW": return "Malawi";
77
		case "ML": return "Mali";
78
		case "MR": return "Mauritania";
79
		case "MU": return "Mauritius";
80
		case "YT": return "Mayotte";
81
		case "MA": return "Morocco";
82
		case "MZ": return "Mozambique";
83
		case "NA": return "Namibia";
84
		case "NE": return "Niger";
85
		case "NG": return "Nigeria";
86
		case "CG": return "Republic of the Congo";
87
		case "RW": return "Rwanda";
88
		case "RE": return "Réunion";
89
		case "SH": return "Saint Helena";
90
		case "SN": return "Senegal";
91
		case "SC": return "Seychelles";
92
		case "SL": return "Sierra Leone";
93
		case "SO": return "Somalia";
94
		case "ZA": return "South Africa";
95
		case "SS": return "South Sudan";
96
		case "SD": return "Sudan";
97
		case "ST": return "São Tomé and Príncipe";
98
		case "TZ": return "Tanzania";
99
		case "TG": return "Togo";
100
		case "TN": return "Tunisia";
101
		case "UG": return "Uganda";
102
		case "EH": return "Western Sahara";
103
		case "ZM": return "Zambia";
104
		case "ZW": return "Zimbabwe";
105
		case "AQ": return "Antarctica";
106
		case "BV": return "Bouvet Island";
107
		case "TF": return "French Southern Territories";
108
		case "HM": return "Heard Island and McDonald Islands";
109
		case "GS": return "South Georgia and the South Sandwich Islands";
110
		case "AF": return "Afghanistan";
111
		case "AM": return "Armenia";
112
		case "AZ": return "Azerbaijan";
113
		case "BH": return "Bahrain";
114
		case "BD": return "Bangladesh";
115
		case "BT": return "Bhutan";
116
		case "IO": return "British Indian Ocean Territory";
117
		case "BN": return "Brunei";
118
		case "KH": return "Cambodia";
119
		case "CN": return "China";
120
		case "CC": return "Cocos [Keeling] Islands";
121
		case "GE": return "Georgia";
122
		case "JO": return "Hashemite Kingdom of Jordan";
123
		case "HK": return "Hong Kong";
124
		case "IN": return "India";
125
		case "ID": return "Indonesia";
126
		case "IR": return "Iran";
127
		case "IQ": return "Iraq";
128
		case "IL": return "Israel";
129
		case "JP": return "Japan";
130
		case "KZ": return "Kazakhstan";
131
		case "KW": return "Kuwait";
132
		case "KG": return "Kyrgyzstan";
133
		case "LA": return "Laos";
134
		case "LB": return "Lebanon";
135
		case "MO": return "Macao";
136
		case "MY": return "Malaysia";
137
		case "MV": return "Maldives";
138
		case "MN": return "Mongolia";
139
		case "MM": return "Myanmar";
140
		case "NP": return "Nepal";
141
		case "KP": return "North Korea";
142
		case "OM": return "Oman";
143
		case "PK": return "Pakistan";
144
		case "PS": return "Palestine";
145
		case "PH": return "Philippines";
146
		case "QA": return "Qatar";
147
		case "SA": return "Saudi Arabia";
148
		case "SG": return "Singapore";
149
		case "KR": return "South Korea";
150
		case "LK": return "Sri Lanka";
151
		case "SY": return "Syria";
152
		case "TW": return "Taiwan";
153
		case "TJ": return "Tajikistan";
154
		case "TH": return "Thailand";
155
		case "TR": return "Turkey";
156
		case "TM": return "Turkmenistan";
157
		case "AE": return "United Arab Emirates";
158
		case "UZ": return "Uzbekistan";
159
		case "VN": return "Vietnam";
160
		case "YE": return "Yemen";
161
		case "AL": return "Albania";
162
		case "AD": return "Andorra";
163
		case "AT": return "Austria";
164
		case "BY": return "Belarus";
165
		case "BE": return "Belgium";
166
		case "BA": return "Bosnia and Herzegovina";
167
		case "BG": return "Bulgaria";
168
		case "HR": return "Croatia";
169
		case "CY": return "Cyprus";
170
		case "CZ": return "Czechia";
171
		case "DK": return "Denmark";
172
		case "EE": return "Estonia";
173
		case "FO": return "Faroe Islands";
174
		case "FI": return "Finland";
175
		case "FR": return "France";
176
		case "DE": return "Germany";
177
		case "GI": return "Gibraltar";
178
		case "GR": return "Greece";
179
		case "GG": return "Guernsey";
180
		case "HU": return "Hungary";
181
		case "IS": return "Iceland";
182
		case "IE": return "Ireland";
183
		case "IM": return "Isle of Man";
184
		case "IT": return "Italy";
185
		case "JE": return "Jersey";
186
		case "XK": return "Kosovo";
187
		case "LV": return "Latvia";
188
		case "LI": return "Liechtenstein";
189
		case "LU": return "Luxembourg";
190
		case "MT": return "Malta";
191
		case "MC": return "Monaco";
192
		case "ME": return "Montenegro";
193
		case "NL": return "Netherlands";
194
		case "MK": return "North Macedonia";
195
		case "NO": return "Norway";
196
		case "PL": return "Poland";
197
		case "PT": return "Portugal";
198
		case "LT": return "Republic of Lithuania";
199
		case "MD": return "Republic of Moldova";
200
		case "RO": return "Romania";
201
		case "RU": return "Russia";
202
		case "SM": return "San Marino";
203
		case "RS": return "Serbia";
204
		case "SK": return "Slovakia";
205
		case "SI": return "Slovenia";
206
		case "ES": return "Spain";
207
		case "SJ": return "Svalbard and Jan Mayen";
208
		case "SE": return "Sweden";
209
		case "CH": return "Switzerland";
210
		case "UA": return "Ukraine";
211
		case "GB": return "United Kingdom";
212
		case "VA": return "Vatican City";
213
		case "AX": return "Åland";
214
		case "AI": return "Anguilla";
215
		case "AG": return "Antigua and Barbuda";
216
		case "AW": return "Aruba";
217
		case "BS": return "Bahamas";
218
		case "BB": return "Barbados";
219
		case "BZ": return "Belize";
220
		case "BM": return "Bermuda";
221
		case "BQ": return "Bonaire, Sint Eustatius, and Saba";
222
		case "VG": return "British Virgin Islands";
223
		case "CA": return "Canada";
224
		case "KY": return "Cayman Islands";
225
		case "CR": return "Costa Rica";
226
		case "CU": return "Cuba";
227
		case "CW": return "Curaçao";
228
		case "DM": return "Dominica";
229
		case "DO": return "Dominican Republic";
230
		case "SV": return "El Salvador";
231
		case "GL": return "Greenland";
232
		case "GD": return "Grenada";
233
		case "GP": return "Guadeloupe";
234
		case "GT": return "Guatemala";
235
		case "HT": return "Haiti";
236
		case "HN": return "Honduras";
237
		case "JM": return "Jamaica";
238
		case "MQ": return "Martinique";
239
		case "MX": return "Mexico";
240
		case "MS": return "Montserrat";
241
		case "NI": return "Nicaragua";
242
		case "PA": return "Panama";
243
		case "PR": return "Puerto Rico";
244
		case "BL": return "Saint Barthélemy";
245
		case "LC": return "Saint Lucia";
246
		case "MF": return "Saint Martin";
247
		case "PM": return "Saint Pierre and Miquelon";
248
		case "VC": return "Saint Vincent and the Grenadines";
249
		case "SX": return "Sint Maarten";
250
		case "KN": return "St Kitts and Nevis";
251
		case "TT": return "Trinidad and Tobago";
252
		case "TC": return "Turks and Caicos Islands";
253
		case "VI": return "U.S. Virgin Islands";
254
		case "US": return "United States";
255
		case "AS": return "American Samoa";
256
		case "AU": return "Australia";
257
		case "CX": return "Christmas Island";
258
		case "CK": return "Cook Islands";
259
		case "TL": return "Democratic Republic of Timor-Leste";
260
		case "FM": return "Federated States of Micronesia";
261
		case "FJ": return "Fiji";
262
		case "PF": return "French Polynesia";
263
		case "GU": return "Guam";
264
		case "KI": return "Kiribati";
265
		case "MH": return "Marshall Islands";
266
		case "NR": return "Nauru";
267
		case "NC": return "New Caledonia";
268
		case "NZ": return "New Zealand";
269
		case "NU": return "Niue";
270
		case "NF": return "Norfolk Island";
271
		case "MP": return "Northern Mariana Islands";
272
		case "PW": return "Palau";
273
		case "PG": return "Papua New Guinea";
274
		case "PN": return "Pitcairn Islands";
275
		case "WS": return "Samoa";
276
		case "SB": return "Solomon Islands";
277
		case "TK": return "Tokelau";
278
		case "TO": return "Tonga";
279
		case "TV": return "Tuvalu";
280
		case "UM": return "U.S. Minor Outlying Islands";
281
		case "VU": return "Vanuatu";
282
		case "WF": return "Wallis and Futuna";
283
		case "AR": return "Argentina";
284
		case "BO": return "Bolivia";
285
		case "BR": return "Brazil";
286
		case "CL": return "Chile";
287
		case "CO": return "Colombia";
288
		case "EC": return "Ecuador";
289
		case "FK": return "Falkland Islands";
290
		case "GF": return "French Guiana";
291
		case "GY": return "Guyana";
292
		case "PY": return "Paraguay";
293
		case "PE": return "Peru";
294
		case "SR": return "Suriname";
295
		case "UY": return "Uruguay";
296
		case "VE": return "Venezuela";
297
	}
0 ignored issues
show
Comprehensibility introduced by
There is no default case in this switch, so nothing gets returned when all cases fail. You might want to consider adding a default or return undefined explicitly.
Loading history...
298
}
299
300
function getCountryFlag(countryCode) {
301
	return sodium.to_string(new Uint8Array([
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
302
		240, 159, 135, 166 + countryCode.codePointAt(0) - 65,
303
		240, 159, 135, 166 + countryCode.codePointAt(1) - 65
304
	]));
305
}
306
307
function getClockIcon(d) {
308
	const h24 = d.getUTCHours();
309
	let h12 = (h24 === 0 ? 12 : ((h24 > 12) ? h24 - 12 : h24));
310
311
	const m60 = (d.getUTCMinutes() * 60) + d.getUTCSeconds();
312
	let m30 = 0;
313
	if (m60 <= 900) { // <= 15: round down to this hour
314
		m30 = 0;
315
	} else if (m60 > 900 && m60 < 2700) { // 15..45: round to half-past this hour
316
		m30 = 12;
317
	} else { // >= 45: round up to next hour
318
		h12++;
319
		m30 = 0;
320
	}
321
322
	return "&#" + ((128335 + h12) + m30) + ";";
323
}
324
325
function clearDisplay() {
326
	let el = document.getElementById("midright").getElementsByTagName("img");
327
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("audio");
328
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("video");
329
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("embed");
330
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("iframe");
331
	if (el.length !== 1) return;
332
333
	URL.revokeObjectURL(el[0].src);
1 ignored issue
show
Bug introduced by
The variable URL seems to be never declared. If this is a global, consider adding a /** global: URL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
334
	el[0].remove();
335
}
336
337
function displayFile(num) {
338
	const fileType = ae.GetUplMsgType(num);
339
340
	if (!fileType) { // Download
341
		const a = document.createElement("a");
342
		a.href = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
2 ignored issues
show
Bug introduced by
The variable URL seems to be never declared. If this is a global, consider adding a /** global: URL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Bug introduced by
The variable Blob seems to be never declared. If this is a global, consider adding a /** global: Blob */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
343
		a.download = ae.GetUplMsgTitle(num);
344
		a.click();
345
346
		URL.revokeObjectURL(a.href);
347
		a.href = "";
348
		a.download = "";
349
		return;
350
	}
351
352
	clearDisplay();
353
354
	document.getElementById("midright").scroll(0, 0);
355
	document.getElementById("midright").setAttribute("data-msgid", ae.GetUplMsgIdHex(num));
356
	document.getElementById("btn_reply").disabled = true;
357
	document.getElementById("btn_mdele").disabled = false;
358
	document.getElementById("midright").children[0].hidden = true;
359
	document.getElementById("midright").children[1].textContent = ae.GetUplMsgTitle(num);
360
361
	switch (fileType) {
362
		case "text": {
363
			document.getElementById("midright").children[2].hidden = false;
364
			document.getElementById("midright").children[2].textContent = sodium.to_string(ae.GetUplMsgBody(num));
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
365
		break;}
366
367
		case "image": {
368
			document.getElementById("midright").children[2].hidden = true;
369
			const img = document.createElement("img");
370
			img.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
371
			document.getElementById("midright").appendChild(img);
372
373
			img.onclick = function() {
374
				if (!document.fullscreen)
375
					img.requestFullscreen();
376
				else
377
					document.exitFullscreen();
378
			};
379
		break;}
380
381
		case "audio": {
382
			document.getElementById("midright").children[2].hidden = true;
383
			const el = document.createElement("audio");
384
			el.controls = "controls";
385
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
386
			document.getElementById("midright").appendChild(el);
387
		break;}
388
389
		case "video": {
390
			document.getElementById("midright").children[2].hidden = true;
391
			const el = document.createElement("video");
392
			el.controls = "controls";
393
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
394
			document.getElementById("midright").appendChild(el);
395
		break;}
396
397
		case "pdf": {
398
			document.getElementById("midright").children[2].hidden = true;
399
			const el = document.createElement("embed");
400
			el.type = "application/pdf";
401
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer], {type: "application/pdf"}));
402
			document.getElementById("midright").appendChild(el);
403
		break;}
404
405
		case "html": {
406
			document.getElementById("midright").children[2].hidden = true;
407
			const el = document.createElement("iframe");
408
			el.allow = "";
409
			el.sandbox = "";
410
			el.csp = "base-uri 'none'; child-src 'none'; connect-src 'none'; default-src 'none'; font-src 'none'; form-action 'none'; frame-ancestors 'none'; frame-src 'none'; img-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'none'; style-src 'none'; worker-src 'none';";
411
			el.srcdoc = sodium.to_string(ae.GetUplMsgBody(num).buffer);
412
			document.getElementById("midright").appendChild(el);
413
		break;}
414
	}
415
}
416
417
function displayMsg(isInt, num) {
418
	clearDisplay();
419
420
	document.getElementById("btn_mdele").disabled = false;
421
	document.getElementById("midright").scroll(0, 0);
422
	document.getElementById("midright").setAttribute("data-msgid", isInt? ae.GetIntMsgIdHex(num) : ae.GetExtMsgIdHex(num));
423
424
	const ts = isInt? ae.GetIntMsgTime(num) : ae.GetExtMsgTime(num);
425
426
	document.getElementById("btn_reply").disabled = false;
427
	document.getElementById("btn_reply").onclick = function() {
428
		document.getElementById("write_recv").value = isInt? ae.GetIntMsgFrom(num) : ae.GetExtMsgReplyAddress(num);
429
		document.getElementById("write_subj").value = isInt? ae.GetIntMsgTitle(num) : ae.GetExtMsgTitle(num);
430
		if (!document.getElementById("write_subj").value.startsWith("Re:")) document.getElementById("write_subj").value = "Re: " + document.getElementById("write_subj").value;
431
		document.querySelector("#write2_pkey > input").value = isInt? ae.GetIntMsgFromPk(num) : "";
432
433
		document.getElementById("write_recv").readOnly = !isInt;
434
		document.getElementById("write_subj").readOnly = !isInt;
435
		document.getElementById("write_subj").setAttribute("data-replyid", isInt? "" : ae.GetExtMsgHdrId(num));
436
437
		tabs[TAB_WRITE].cur = 0;
438
		document.getElementById("btn_write").disabled = false;
439
		document.getElementById("btn_write").click();
440
		document.getElementById("write_body").focus();
441
442
		for (const opt of document.getElementById("write_from").options) {
443
			if (opt.value === (isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgEnvTo(num))) {
444
				opt.selected = true;
445
			}
446
		}
447
	};
448
449
	document.getElementById("midright").children[0].hidden = false;
450
	document.getElementById("midright").children[2].hidden = false;
451
452
	if (isInt) {
453
		document.getElementById("midright").children[1].textContent = ae.GetIntMsgTitle(num);
454
		document.getElementById("midright").children[2].textContent = ae.GetIntMsgBody(num);
455
	} else {
456
		document.getElementById("midright").children[2].innerHTML = "";
457
458
		const headers = document.createElement("p");
459
		headers.textContent = ae.GetExtMsgHeaders(num);
460
		headers.className = "mono";
461
		headers.hidden = !showHeaders;
462
		document.getElementById("midright").children[2].appendChild(headers);
463
464
		const body = document.createElement("p");
465
		body.textContent = ae.GetExtMsgBody(num);
466
		document.getElementById("midright").children[2].appendChild(body);
467
468
		document.getElementById("midright").children[1].textContent = ae.GetExtMsgTitle(num);
469
		document.getElementById("midright").children[1].onclick = function() {showHeaders = !showHeaders; headers.hidden = !showHeaders;};
470
		document.getElementById("midright").children[1].style.cursor = "pointer";
471
	}
472
473
	document.getElementById("readmsg_envto").textContent = isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgEnvTo(num);
474
	document.getElementById("readmsg_hdrto").textContent = isInt ? "" : ae.GetExtMsgHdrTo(num);
475
476
	const tzOs = new Date().getTimezoneOffset();
477
	const tz = ((tzOs < 0) ? "+" : "-") + Math.floor(tzOs / -60).toString().padStart(2, "0") + (tzOs % 60 * -1).toString().padStart(2, "0");
478
	const msgDate = new Date((ts * 1000) + (tzOs * -60000));
479
480
	document.getElementById("readmsg_date").children[0].innerHTML = getClockIcon(msgDate);
481
	document.getElementById("readmsg_date").children[1].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ") + " " + tz;
482
483
	if (!isInt) {
484
		document.getElementById("readmsg_ip").hidden = false;
485
		document.getElementById("readmsg_country").hidden = false;
486
		document.getElementById("readmsg_tls").hidden = false;
487
		document.getElementById("readmsg_greet").hidden = false;
488
489
		const cc = ae.GetExtMsgCountry(num);
490
491
		document.getElementById("readmsg_country").textContent = getCountryFlag(cc);
492
		document.getElementById("readmsg_country").title = getCountryName(cc);
493
		document.getElementById("readmsg_ip").children[1].textContent = ae.GetExtMsgIp(num);
494
		document.getElementById("readmsg_rdns").children[0].textContent = ae.GetExtMsgRdns(num);
495
		document.getElementById("readmsg_tls").children[0].textContent = ae.GetExtMsgTLS(num);
496
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetExtMsgGreet(num);
497
		document.getElementById("readmsg_envfrom").textContent = ae.GetExtMsgEnvFrom(num);
498
		document.getElementById("readmsg_hdrfrom").textContent = ae.GetExtMsgHdrFrom(num);
499
500
		let flagText = "";
501
		if (!ae.GetExtMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
502
		if (!ae.GetExtMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
503
		if (!ae.GetExtMsgFlagPExt(num)) flagText += "<abbr title=\"The sender did not use the Extended (ESMTP) protocol\">SMTP</abbr> ";
504
		if (!ae.GetExtMsgFlagQuit(num)) flagText += "<abbr title=\"The sender did not issue the required QUIT command\">QUIT</abbr> ";
505
		if (ae.GetExtMsgFlagRare(num)) flagText += "<abbr title=\"The sender issued unusual command(s)\">RARE</abbr> ";
506
		if (ae.GetExtMsgFlagFail(num)) flagText += "<abbr title=\"The sender issued invalid command(s)\">FAIL</abbr> ";
507
		if (ae.GetExtMsgFlagPErr(num)) flagText += "<abbr title=\"The sender violated the protocol\">PROT</abbr> ";
508
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
509
	} else {
510
		document.getElementById("readmsg_ip").hidden = true;
511
		document.getElementById("readmsg_country").hidden = true;
512
		document.getElementById("readmsg_greet").hidden = true;
513
514
		document.getElementById("readmsg_tls").hidden = false;
515
		document.getElementById("readmsg_tls").children[0].textContent = ae.GetIntMsgFromPk(num);
516
517
		let symbol = "<span title=\"Invalid level\">&#x26a0;</span>";
518
		if (ae.GetIntMsgFrom(num) === "system") {if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"System\">&#x1f162;</span>";} // S (System)
519
		else if (ae.GetIntMsgLevel(num) === 0) symbol = "<span title=\"Level 0 User\">&#x1f10c;</span>"; // 0
520
		else if (ae.GetIntMsgLevel(num) === 1) symbol = "<span title=\"Level 1 User\">&#x278a;</span>"; // 1
521
		else if (ae.GetIntMsgLevel(num) === 2) symbol = "<span title=\"Level 2 User\">&#x278b;</span>"; // 2
522
		else if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"Administrator\">&#x1f150;</span>"; // A (Admin)
523
		document.getElementById("readmsg_envfrom").innerHTML = symbol + " " + ae.GetIntMsgFrom(num);
524
525
		let flagText = "";
526
		if (!ae.GetIntMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
527
		if (!ae.GetIntMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
528
		if (ae.GetIntMsgFlagE2ee(num)) flagText += "<abbr title=\"End-to-end encrypted\">E2EE</abbr> ";
529
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
530
	}
531
}
532
533
// Interface
534
function addMsg(isInt, i) {
535
	const row = document.getElementById("tbl_inbox").insertRow(-1);
536
	row.setAttribute("data-msgid", isInt? ae.GetIntMsgIdHex(i) : ae.GetExtMsgIdHex(i));
537
538
	const cellTime = row.insertCell(-1);
539
	const ts = isInt? ae.GetIntMsgTime(i) : ae.GetExtMsgTime(i);
540
	cellTime.setAttribute("data-ts", ts);
541
	cellTime.textContent = new Date((ts * 1000) + (new Date().getTimezoneOffset() * -60000)).toISOString().slice(0, 10);
542
543
	const cellSubj = row.insertCell(-1);
544
	cellSubj.textContent = isInt? ae.GetIntMsgTitle(i) : ae.GetExtMsgTitle(i);
545
546
	if (isInt) {
547
		const cellSnd = row.insertCell(-1);
548
		cellSnd.textContent = ae.GetIntMsgFrom(i);
549
		cellSnd.className = (ae.GetIntMsgFrom(i).length === 16) ? "mono" : "";
550
	} else {
551
		const from1 = ae.GetExtMsgEnvFrom(i);
552
		const from2 = from1.substring(from1.indexOf("@") + 1);
553
		const cc = ae.GetExtMsgCountry(i);
554
		const cellSnd1 = row.insertCell(-1);
555
		cellSnd1.textContent = from1.substring(0, from1.indexOf("@"));
556
557
		const flag = document.createElement("abbr");
558
		flag.textContent = getCountryFlag(cc);
559
		flag.title = getCountryName(cc);
560
561
		const fromText = document.createElement("span");
562
		fromText.textContent = " " + from2;
563
564
		const cellSnd2 = row.insertCell(-1);
565
		cellSnd2.appendChild(flag);
566
		cellSnd2.appendChild(fromText);
567
	}
568
569
	row.onclick = function() {
570
		displayMsg(isInt, i);
571
	};
572
}
573
574
function getRowsPerPage() {
575
	const tbl = document.getElementById("tbl_inbox");
576
	tbl.innerHTML = "";
577
	const row = tbl.insertRow(-1);
578
	const cell = row.insertCell(-1);
579
	cell.textContent = "0";
580
581
	const rowsPerPage = Math.floor(getComputedStyle(document.getElementById("div_inbox")).height.replace("px", "") / getComputedStyle(document.querySelector("#tbl_inbox > tbody > tr:first-child")).height.replace("px", "")) - 1; // -1 allows space for 'load more'
582
	tbl.innerHTML = "";
583
	return rowsPerPage;
584
}
585
586
function addMessages() {
587
	const rowsPerPage = getRowsPerPage();
588
	let skipMsgs = rowsPerPage * tabs[TAB_INBOX].cur;
589
590
	const maxExt = ae.GetExtMsgCount();
591
	const maxInt = ae.GetIntMsgCount();
592
593
	tabs[TAB_INBOX].max = Math.floor((maxExt + maxInt - 1) / rowsPerPage);
594
595
	let numExt = 0;
596
	let numInt = 0;
597
	let numAdd = 0;
598
599
	while (numAdd < rowsPerPage) {
600
		const tsInt = (numInt < maxInt) ? ae.GetIntMsgTime(numInt) : -1;
601
		const tsExt = (numExt < maxExt) ? ae.GetExtMsgTime(numExt) : -1;
602
		if (tsInt === -1 && tsExt === -1) break;
603
604
		if (tsInt !== -1 && (tsExt === -1 || tsInt > tsExt)) {
605
			if (skipMsgs > 0) skipMsgs--; else {addMsg(true, numInt); numAdd++;}
606
			numInt++;
607
		} else if (tsExt !== -1) {
608
			if (skipMsgs > 0) skipMsgs--; else {addMsg(false, numExt); numAdd++;}
609
			numExt++;
610
		}
611
	}
612
613
	if (ae.GetReadyMsgBytes() < ae.GetTotalMsgBytes()) {
614
		const inbox = document.getElementById("tbl_inbox");
615
		const row = inbox.insertRow(-1);
616
		const cell = row.insertCell(-1);
617
		cell.textContent = "Load more (" + Math.round((ae.GetTotalMsgBytes() - ae.GetReadyMsgBytes()) / 1024) + " KiB left)";
618
619
		row.onclick = function() {
620
			this.onclick = "";
621
622
			ae.Message_Browse(false, false, function(successBrowse) {
623
				document.getElementById("tbl_inbox").style.opacity = 1;
624
625
				if (successBrowse) {
626
					addMessages();
627
					addUploads();
628
					addSent();
629
					if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
630
				}
631
			});
632
		};
633
	}
634
}
635
636
function addUploads() {
637
	const tbl = document.getElementById("tbd_uploads");
638
	tbl.innerHTML = "";
639
640
	for (let i = 0; i < ae.GetUplMsgCount(); i++) {
641
		const row = tbl.insertRow(-1);
642
		row.setAttribute("data-msgid", ae.GetUplMsgIdHex(i));
643
644
		let cell;
645
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetUplMsgTime(i) * 1000).toISOString().slice(0, 10);
646
647
		cell = row.insertCell(-1); cell.textContent = ae.GetUplMsgTitle(i);
648
		cell.onclick = function() {displayFile(this.parentElement.rowIndex - 1);};
649
650
		cell = row.insertCell(-1); cell.textContent = (ae.GetUplMsgBytes(i) / 1024).toFixed(1);
651
652
		cell = row.insertCell(-1);
653
		if (ae.GetUplMsgIdHex(i)) {
654
			cell.innerHTML = "<button data-msgid=\"" + ae.GetUplMsgIdHex(i) + "\" type=\"button\">X</button>";
655
656
			cell.children[0].onclick = function() {
657
				const tr = this.parentElement.parentElement;
658
				ae.Message_Delete(this.getAttribute("data-msgid"), function(success) {
659
					if (success) tr.remove();
660
				});
661
			};
662
		}
663
	}
664
}
665
666
function displayOutMsg(num) {
667
	clearDisplay();
668
	document.getElementById("midright").scroll(0, 0);
669
	document.getElementById("midright").setAttribute("data-msgid", ae.GetOutMsgIdHex(num));
670
	document.getElementById("btn_reply").disabled = true;
671
	document.getElementById("btn_mdele").disabled = false;
672
	document.getElementById("midright").children[0].hidden = false;
673
	document.getElementById("midright").children[2].hidden = false;
674
675
	document.getElementById("midright").children[1].textContent = ae.GetOutMsgSubj(num);
676
	document.getElementById("midright").children[2].textContent = ae.GetOutMsgBody(num);
677
678
	document.getElementById("readmsg_envfrom").textContent = ae.GetOutMsgFrom(num);
679
	document.getElementById("readmsg_envto").textContent = ae.GetOutMsgMxDom(num);
680
	document.getElementById("readmsg_hdrto").textContent = ae.GetOutMsgEnvTo(num);
681
682
	const ts = ae.GetOutMsgTime(num);
683
	const tzOs = new Date().getTimezoneOffset();
684
	const tz = ((tzOs < 0) ? "+" : "-") + Math.floor(tzOs / -60).toString().padStart(2, "0") + (tzOs % 60 * -1).toString().padStart(2, "0");
685
	document.getElementById("readmsg_date").children[1].textContent = new Date((ts * 1000) + (tzOs * -60000)).toISOString().slice(0, 19).replace("T", " ") + " " + tz;
686
687
	const isInt = ae.GetOutMsgIsInt(num);
688
	document.getElementById("readmsg_ip").hidden      = isInt;
689
	document.getElementById("readmsg_country").hidden = isInt;
690
	document.getElementById("readmsg_tls").hidden     = isInt;
691
	document.getElementById("readmsg_greet").hidden   = isInt;
692
693
	if (!isInt) {
694
//		const cc = ae.GetExtMsgCountry(num);
695
696
		document.getElementById("readmsg_ip").children[0].textContent = ae.GetOutMsgIp(num);
697
//		document.getElementById("readmsg_country").textContent = getCountryFlag(cc) + " " + getCountryName(cc);
698
//		document.getElementById("readmsg_tls").children[0].textContent = ae.GetOutMsgTLS(num);
699
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetOutMsgGreet(num);
700
	}
701
702
	let flagText = "";
703
	if (!ae.GetOutMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
704
	if (!ae.GetOutMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
705
	if (ae.GetOutMsgFlagE2ee(num)) flagText += "<abbr title=\"End-to-end encrypted\">E2EE</abbr> ";
706
	document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
707
}
708
709
function addSent() {
710
	const tbl = document.getElementById("tbl_drbox");
711
	tbl.innerHTML = "";
712
713
	for (let i = 0; i < ae.GetOutMsgCount(); i++) {
714
		const row = tbl.insertRow(-1);
715
		row.setAttribute("data-msgid", ae.GetOutMsgIdHex(i));
716
717
		let cell;
718
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetOutMsgTime(i) * 1000).toISOString().slice(0, 10);
719
		cell = row.insertCell(-1); cell.textContent = ae.GetOutMsgSubj(i);
720
		row.onclick = function() {displayOutMsg(i);};
721
	}
722
}
723
724
function updateAddressCounts() {
725
	document.getElementById("limit_normal").textContent = (ae.GetAddressCountNormal() + "/" + ae.GetLimitNormalA(ae.GetUserLevel())).padStart(ae.GetLimitNormalA(ae.GetUserLevel()) > 9 ? 5 : 1);
726
	document.getElementById("limit_shield").textContent = (ae.GetAddressCountShield() + "/" + ae.GetLimitShieldA(ae.GetUserLevel())).padStart(ae.GetLimitShieldA(ae.GetUserLevel()) > 9 ? 5 : 1);
727
	document.getElementById("limit_total").textContent = ((ae.GetAddressCountNormal() + ae.GetAddressCountShield()) + "/" + ae.GetAddrPerUser()).padStart(5);
728
729
	const limitReached = (ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31);
730
	document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel()));
731
	document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel()));
732
}
733
734
function adjustLevel(pubkey, level, c) {
735
	const fs = document.getElementById("fs_accs");
736
	fs.disabled = true;
737
738
	ae.Account_Update(pubkey, level, function(success) {
739
		fs.disabled = false;
740
741
		if (success) {
742
			c[4].textContent = level;
743
			c[5].children[0].disabled = (level === 3);
744
			c[6].children[0].disabled = (level === 0);
745
		}
746
	});
747
}
748
749
function addAccountToTable(i) {
750
	const tblAccs = document.getElementById("tbd_accs");
751
	const row = tblAccs.insertRow(-1);
752
	let cell;
753
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserPkHex(i);
754
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSpace(i);
755
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserNAddr(i);
756
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSAddr(i);
757
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserLevel(i);
758
759
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">+</button>";
760
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent, 10) + 1, c);};
761
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 3);
762
763
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">&minus;</button>";
764
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent, 10) - 1, c);};
765
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 0);
766
767
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">X</button>";
768
	cell.children[0].onclick = function() {
769
		const tr = this.parentElement.parentElement;
770
		ae.Account_Delete(tr.cells[0].textContent, function(success) {
771
			if (success) tr.remove();
772
		});
773
	};
774
}
775
776
function reloadAccount() {
777
	// Limits
778
	const tblLimits = document.getElementById("tbl_limits");
779
	if (ae.IsUserAdmin()) {
780
		for (let i = 0; i < 4; i++) {
781
			tblLimits.rows[i].cells[1].children[0].disabled = false;
782
			tblLimits.rows[i].cells[2].children[0].disabled = false;
783
			tblLimits.rows[i].cells[3].children[0].disabled = false;
784
785
			tblLimits.rows[i].cells[1].children[0].value = ae.GetLimitStorage(i) + 1;
786
			tblLimits.rows[i].cells[2].children[0].value = ae.GetLimitNormalA(i);
787
			tblLimits.rows[i].cells[3].children[0].value = ae.GetLimitShieldA(i);
788
		}
789
	} else {
790
		const lvl = ae.GetUserLevel();
791
		tblLimits.rows[lvl].cells[1].children[0].value = ae.GetLimitStorage(lvl) + 1;
792
		tblLimits.rows[lvl].cells[2].children[0].value = ae.GetLimitNormalA(lvl);
793
		tblLimits.rows[lvl].cells[3].children[0].value = ae.GetLimitShieldA(lvl);
794
	}
795
796
	// Accounts
797
	const tblAccs = document.getElementById("tbd_accs");
798
799
	// All: Our account
800
	const row = tblAccs.insertRow(-1);
801
	let cell;
802
	cell = row.insertCell(-1); cell.textContent = ae.GetUserPkHex();
803
	cell = row.insertCell(-1); cell.textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
804
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountNormal();
805
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountShield();
806
	cell = row.insertCell(-1); cell.textContent = ae.GetUserLevel();
807
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\" disabled=\"disabled\">+</button>";
808
809
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_downme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">&minus;</button>";
810
	cell.children[0].onclick = function() {
811
		const newLevel = parseInt(row.cells[4].textContent, 10) - 1;
812
		ae.Account_Update(ae.GetUserPkHex(), newLevel, function(success) {
813
			if (success) row.cells[4].textContent = newLevel;
814
		});
815
	};
816
817
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_killme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">X</button>";
818
	cell.children[0].onclick = function() {
819
		ae.Account_Delete(ae.GetUserPkHex(), function(success) {
820
			if (success) row.remove();
821
		});
822
	};
823
824
	document.getElementById("txt_reg").disabled = !ae.IsUserAdmin();
825
	document.getElementById("btn_reg").disabled = !ae.IsUserAdmin();
826
827
	// Contacts
828
	for (let i = 0; i < ae.GetContactCount(); i++) {
829
		addContact(
830
			ae.GetContactMail(i),
831
			ae.GetContactName(i),
832
			ae.GetContactNote(i)
833
		);
834
	}
835
836
	// Addresses
837
	for (let i = 0; i < ae.GetAddressCount(); i++) {
838
		addAddress(i);
839
	}
840
841
	updateAddressCounts();
842
	addMessages();
843
	addUploads();
844
	addSent();
845
846
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
847
}
848
849
function deleteAddress(addr) {
850
	let btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
851
	for (let i = 0; i < btns.length; i++) btns[i].disabled = true;
852
853
	let addressToDelete = -1;
854
855
	for (let i = 0; i < ae.GetAddressCount(); i++) {
856
		if (addr === ae.GetAddress(i)) {
857
			addressToDelete = i;
858
			break;
859
		}
860
	}
861
862
	if (addressToDelete === -1) return;
863
864
	ae.Address_Delete(addressToDelete, function(success) {
865
		if (success) {
866
			document.getElementById("tbl_addrs").deleteRow(addressToDelete);
867
			document.getElementById("write_from").remove(addressToDelete);
868
			updateAddressCounts();
869
870
			const limitReached = (ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31);
871
			document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.GetAddressCountNormal() > ae.GetLimitNormalA(ae.GetUserLevel()));
872
			document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.GetAddressCountShield() > ae.GetLimitShieldA(ae.GetUserLevel()));
873
874
			ae.Private_Update(function(success2) {
875
				if (!success2) console.log("Failed to update the Private field");
876
877
				btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
878
				for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
879
			});
880
		} else {
881
			console.log("Failed to delete address");
882
883
			btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
884
			for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
885
		}
886
	});
887
}
888
889 View Code Duplication
function shieldMix(addr) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
890
	let newAddr = "";
891
892
	for (let i = 0; i < 16; i++) {
893
		switch (addr.charAt(i)) {
894
			case '1':
895
				newAddr += "1iIlL".charAt(Math.floor(Math.random() * 5));
896
				break;
897
			case '0':
898
				newAddr += "0oO".charAt(Math.floor(Math.random() * 3));
899
				break;
900
			case 'w':
901
				newAddr += "VvWw".charAt(Math.floor(Math.random() * 4));
902
				break;
903
			default:
904
				newAddr += (Math.random() > 0.5) ? addr.charAt(i) : addr.charAt(i).toUpperCase();
905
		}
906
	}
907
908
	return newAddr;
909
}
910
911
function addAddress(num) {
912
	const addrTable = document.getElementById("tbl_addrs");
913
	const row = addrTable.insertRow(-1);
914
	const cellAddr = row.insertCell(-1);
915
	const cellChk1 = row.insertCell(-1);
916
	const cellChk2 = row.insertCell(-1);
917
	const cellBtnD = row.insertCell(-1);
918
919
	cellAddr.textContent = ae.GetAddress(num);
920
	cellAddr.onclick = function() {
921
		if (cellAddr.textContent.length === 16)
922
			navigator.clipboard.writeText(shieldMix(cellAddr.textContent) + "@" + ae.GetDomainEml());
1 ignored issue
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
923
		else
924
			navigator.clipboard.writeText(cellAddr.textContent + "@" + ae.GetDomainEml());
925
	};
926
927
	cellChk1.innerHTML = ae.GetAddressAccExt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
928
	cellChk2.innerHTML = ae.GetAddressAccInt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
929
930
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
931
	cellBtnD.onclick = function() {deleteAddress(cellAddr.textContent);};
932
933
	const opt = document.createElement("option");
934
	opt.value = cellAddr.textContent;
935
	opt.textContent = cellAddr.textContent + "@" + ae.GetDomainEml();
936
	document.getElementById("write_from").appendChild(opt);
937
}
938
939
document.getElementById("btn_dele").onclick = function() {
940
	this.blur();
941
942
	if (tab === TAB_WRITE) {
943
		tabs[tab].cur = 0;
944
		updateTab();
945
946
		document.getElementById("write_recv").value = "";
947
		document.getElementById("write_subj").value = "";
948
		document.getElementById("write_body").value = "";
949
950
		document.getElementById("write_recv").readOnly = false;
951
		document.getElementById("write_subj").readOnly = false;
952
		document.getElementById("write_subj").setAttribute("data-replyid", "");
953
954
		document.getElementById("write_recv").focus();
955
	}
956
};
957
958
document.getElementById("btn_updt").onclick = function() {
959
	const btn = this;
960
	btn.disabled = true;
961
	btn.blur();
962
963
	if (tab === TAB_INBOX) {
964
		document.getElementById("tbl_inbox").style.opacity = 0.5;
965
966
		ae.Message_Browse(true, false, function(successBrowse) {
967
			document.getElementById("tbl_inbox").style.opacity = 1;
968
969
			if (successBrowse) {
970
				addMessages();
971
				addUploads();
972
				btn.disabled = false;
973
			} else {
974
				console.log("Failed to refresh");
975
				btn.disabled = false;
976
			}
977
		});
978
	}
979
};
980
981
document.getElementById("btn_mdele").onclick = function() {
982
	const btn = this;
983
	btn.blur();
984
	btn.disabled = true;
985
986
	const delId = document.getElementById("midright").getAttribute("data-msgid");
987
	if (!delId) return;
988
989
	ae.Message_Delete(delId, function(success) {
990
		if (success) {
991
			["tbl_inbox", "tbl_drbox", "tbd_uploads"].forEach(function(tbl_name) {
992
				const tbl = document.getElementById(tbl_name);
993
				for (let i = 0; i < tbl.rows.length; i++) {if (tbl.rows[i].getAttribute("data-msgid") === delId) tbl.deleteRow(i);}
994
			});
995
996
			addMessages();
997
			addUploads();
998
			addSent();
999
		} else btn.disabled = false;
1000
	});
1001
};
1002
1003
function addContact(mail, name, note) {
1004
	const tbl = document.getElementById("tbl_ctact");
1005
	const row = tbl.insertRow(-1);
1006
	const cellMail = row.insertCell(-1);
1007
	const cellName = row.insertCell(-1);
1008
	const cellNote = row.insertCell(-1);
1009
	const cellBtnD = row.insertCell(-1);
1010
1011
	cellMail.textContent = mail;
1012
	cellName.textContent = name;
1013
	cellNote.textContent = note;
1014
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
1015
1016
	cellMail.contentEditable = true;
1017
	cellName.contentEditable = true;
1018
	cellNote.contentEditable = true;
1019
1020
	cellBtnD.onclick = function() {row.remove();};
1021
}
1022
1023
document.getElementById("btn_newcontact").onclick = function() {
1024
	addContact("", "", "");
1025
};
1026
1027
document.getElementById("btn_savecontacts").onclick = function() {
1028
	while (ae.GetContactCount() > 0) {
1029
		ae.DeleteContact(0);
1030
	}
1031
1032
	for (const row of document.getElementById("tbl_ctact").rows) {
1033
		ae.AddContact(row.cells[0].textContent, row.cells[1].textContent, row.cells[2].textContent);
1034
	}
1035
1036
	const btn = this;
1037
	btn.disabled = true;
1038
1039
	ae.Private_Update(function(success) {
1040
		btn.disabled = false;
1041
1042
		if (!success) {
1043
			console.log("Failed contacts update");
1044
		}
1045
	});
1046
};
1047
1048
function writeVerify() {
1049
	if (
1050
	   !document.getElementById("write_recv").reportValidity()
1051
	|| !document.getElementById("write_subj").reportValidity()
1052
	|| !document.getElementById("write_body").reportValidity()
1053
	) {tabs[TAB_WRITE].cur = 0; return;}
1054
1055
	document.getElementById("div_write_1").hidden = true;
1056
	document.getElementById("div_write_2").hidden = false;
1057
1058
	document.getElementById("write2_recv").textContent = document.getElementById("write_recv").value;
1059
	document.getElementById("write2_subj").textContent = document.getElementById("write_subj").value;
1060
	document.getElementById("write2_rply").textContent = document.getElementById("write_subj").getAttribute("data-replyid");
1061
	document.getElementById("write2_body").textContent = document.getElementById("write_body").value;
1062
1063
	if (document.getElementById("write_recv").value.indexOf("@") >= 0) {
1064
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value + "@" + ae.GetDomainEml();
1065
		document.getElementById("write2_pkey").hidden = true;
1066
	} else {
1067
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value;
1068
		document.getElementById("write2_pkey").hidden = false;
1069
	}
1070
}
1071
1072
function updateTab() {
1073
	switch (tab) {
1074
		case TAB_INBOX:
1075
			addMessages();
1076
		break;
1077
1078
		case TAB_DRBOX:
1079
			addSent();
1080
		break;
1081
1082
		case TAB_WRITE:
1083
			switch (tabs[tab].cur) {
1084
				case 0:
1085
					document.getElementById("div_write_1").hidden = false;
1086
					document.getElementById("div_write_2").hidden = true;
1087
					document.getElementById("write_body").focus();
1088
					document.querySelector("#write2_send > button").disabled = false;
1089
					document.getElementById("write2_btntxt").textContent = "Send to";
1090
				break;
1091
1092
				case 1:
1093
					writeVerify();
1094
				break;
1095
			}
1096
		break;
1097
1098
		case TAB_NOTES:
1099
			for (let i = 0; i <= tabs[tab].max; i++) {
1100
				document.getElementById("div_notes").children[i].hidden = (i !== tabs[tab].cur);
1101
			}
1102
		break;
1103
1104
		case TAB_TOOLS:
1105
			for (let i = 0; i <= tabs[tab].max; i++) {
1106
				document.getElementById("div_tools").children[i].hidden = (i !== tabs[tab].cur);
1107
			}
1108
		break;
1109
	}
1110
1111
	document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
1112
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1113
}
1114
1115
document.getElementById("btn_left").onclick = function() {
1116
	tabs[tab].cur--;
1117
	if (tabs[tab].cur === 0) this.disabled = true;
1118
	if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
1119
	updateTab();
1120
	this.blur();
1121
};
1122
1123
document.getElementById("btn_rght").onclick = function() {
1124
	tabs[tab].cur++;
1125
	if (tabs[tab].cur === tabs[tab].max) this.disabled = true;
1126
	document.getElementById("btn_left").disabled = false;
1127
	updateTab();
1128
	this.blur();
1129
};
1130
1131
const buttons = document.querySelector("#main1 > .top").getElementsByTagName("button");
1132
for (let i = 0; i < buttons.length; i++) {
1133
	buttons[i].onclick = function() {
1134
		tab = i;
1135
1136
		for (let j = 0; j < buttons.length; j++) {
1137
			document.querySelector("#main1 > .mid").children[j].hidden = (tab !== j);
1138
			buttons[j].disabled = (tab === j);
1139
		}
1140
1141
		document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
0 ignored issues
show
Bug introduced by
The variable tab is changed as part of the for loop for example by i on line 1134. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
1142
		document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1143
		document.getElementById("btn_dele").disabled = !tabs[tab].btnDele;
1144
		document.getElementById("btn_updt").disabled = !tabs[tab].btnUpdt;
1145
1146
		updateTab();
1147
	};
1148
}
1149
1150
function addressCreate(addr) {
1151
	document.getElementById("btn_address_create_normal").disabled = true;
1152
	document.getElementById("btn_address_create_shield").disabled = true;
1153
1154
	ae.Address_Create(addr, function(success1) {
1155
		if (success1) {
1156
			ae.Private_Update(function(success2) {
1157
				updateAddressCounts();
1158
1159
				if (success2) {
1160
					addAddress(ae.GetAddressCount() - 1);
1161
					if (addr !== "SHIELD") document.getElementById("txt_address_create_normal").value = "";
1162
				} else {
1163
					console.log("Failed to update the Private field");
1164
				}
1165
			});
1166
		} else {
1167
			console.log("Failed to add address");
1168
			updateAddressCounts();
1169
		}
1170
	});
1171
}
1172
1173
document.getElementById("btn_address_create_normal").onclick = function() {
1174
	if (ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel()) || ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31) return;
1175
1176
	const txtNewAddr = document.getElementById("txt_address_create_normal");
1177
	if (!txtNewAddr.reportValidity()) return;
1178
1179
	addressCreate(txtNewAddr.value);
1180
};
1181
1182
document.getElementById("btn_address_create_shield").onclick = function() {
1183
	if (ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel()) || ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31) return;
1184
1185
	addressCreate("SHIELD");
1186
};
1187
1188
document.getElementById("btn_address_update").onclick = function() {
1189
	const btn = this;
1190
	btn.disabled = true;
1191
1192
	const rows = document.getElementById("tbl_addrs").rows;
1193
1194
	for (let i = 0; i < rows.length; i++) {
1195
		ae.SetAddressAccExt(i, rows[i].getElementsByTagName("input")[0].checked);
1196
		ae.SetAddressAccInt(i, rows[i].getElementsByTagName("input")[1].checked);
1197
	}
1198
1199
	ae.Address_Update(function(success) {
1200
		if (!success) console.log("Address/Update failed");
1201
		btn.disabled = false;
1202
	});
1203
};
1204
1205
document.getElementById("btn_reg").onclick = function() {
1206
	const btn = document.getElementById("btn_reg");
1207
	const txt = document.getElementById("txt_reg");
1208
	if (!txt.reportValidity()) return;
1209
	btn.disabled = true;
1210
1211
	ae.Account_Create(txt.value, function(success) {
1212
		if (success) {
1213
			addAccountToTable(ae.Admin_GetUserCount() - 1);
1214
			txt.value = "";
1215
		}
1216
1217
		btn.disabled = false;
1218
	});
1219
};
1220
1221
document.getElementById("chk_downme").onclick = function() {document.getElementById("btn_downme").disabled = !this.checked;};
1222
document.getElementById("chk_killme").onclick = function() {document.getElementById("btn_killme").disabled = !this.checked;};
1223
1224
document.getElementById("btn_notepad_saveupl").onclick = function() {
1225
	const np = document.getElementById("txt_notepad");
1226
	np.disabled = true;
1227
1228
	let fname = prompt("Save as...", "Untitled");
0 ignored issues
show
Debugging Code Best Practice introduced by
The prompt UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
1229
	if (!fname.endsWith(".txt")) fname += ".txt";
1230
1231
	ae.Message_Upload(fname, np.value, function(success) {
1232
		if (success) {
1233
			np.value = "";
1234
			addUploads();
1235
			document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
1236
		}
1237
1238
		console.log("Failed to add text");
1239
		np.disabled = false;
1240
	});
1241
};
1242
1243
document.getElementById("btn_upload").onclick = function() {
1244
	const btn = this;
1245
	const fileSelector = document.createElement("input");
1246
	fileSelector.type = "file";
1247
	fileSelector.click();
1248
1249
	fileSelector.onchange = function() {
1250
		btn.disabled = true;
1251
1252
		const reader = new FileReader();
1 ignored issue
show
Bug introduced by
The variable FileReader seems to be never declared. If this is a global, consider adding a /** global: FileReader */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1253
		reader.onload = function() {
1254
			ae.Message_Upload(fileSelector.files[0].name, new Uint8Array(reader.result), function(success) {
1255
				if (success) {
1256
					addUploads();
1257
					document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
1258
				} else {
1259
					console.log("Failed upload");
1260
				}
1261
1262
				btn.disabled = false;
1263
			});
1264
		};
1265
1266
		reader.readAsArrayBuffer(fileSelector.files[0]);
1267
	};
1268
};
1269
1270
document.getElementById("btn_pg").onclick = function() {
1271
	localStorage.greeting = document.getElementById("txt_pg").value;
1 ignored issue
show
Bug introduced by
The variable localStorage seems to be never declared. If this is a global, consider adding a /** global: localStorage */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1272
};
1273
1274
document.querySelector("#write2_send > button").onclick = function() {
1275
	const btn = this;
1276
	btn.disabled = true;
1277
	document.getElementById("write2_btntxt").textContent = "Sending to";
1278
1279
	ae.Message_Create(
1280
		document.getElementById("write_subj").value,
1281
		document.getElementById("write_body").value,
1282
		document.getElementById("write_from").value,
1283
		document.getElementById("write_recv").value,
1284
		document.getElementById("write_subj").getAttribute("data-replyid"),
1285
		(document.getElementById("write2_recv").textContent.indexOf("@") > 0) ? null : sodium.from_base64(document.querySelector("#write2_pkey > input").value, sodium.base64_variants.ORIGINAL_NO_PADDING),
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1286
		function(success) {
1287
			if (success) {
1288
				document.getElementById("write2_btntxt").textContent = "Successfully sent to";
1289
				document.getElementById("write_recv").value = "";
1290
				document.getElementById("write_subj").value = "";
1291
				document.getElementById("write_body").value = "";
1292
			} else {
1293
				document.getElementById("write2_btntxt").textContent = "Retry sending to";
1294
				btn.disabled = false;
1295
			}
1296
		}
1297
	);
1298
};
1299
1300
document.getElementById("txt_skey").onkeyup = function(event) {
1301
	if (event.key === "Enter") {
1302
		event.preventDefault();
1303
		document.getElementById("btn_enter").click();
1304
	}
1305
};
1306
1307
document.getElementById("btn_enter").onclick = function() {
1308
	const txtSkey = document.getElementById("txt_skey");
1309
	if (!txtSkey.reportValidity()) return;
1310
1311
	const btn = this;
1312
	btn.disabled = true;
1313
	document.getElementById("txt_skey").style.background = "#233";
1314
1315
	ae.SetKeys(txtSkey.value, function(successSetKeys) {
1316
		if (successSetKeys) {
1317
			ae.Message_Browse(false, true, function(successBrowse) {
1318
				if (successBrowse) {
1319
					txtSkey.value = "";
1320
					document.getElementById("div_begin").hidden = true;
1321
					document.getElementById("div_main").style.display = "grid";
1322
					reloadAccount();
1323
1324
					if (ae.IsUserAdmin()) {
1325
						ae.Account_Browse(function(successAcc) {
1326
							if (successAcc) {for (let i = 0; i < ae.Admin_GetUserCount(); i++) {addAccountToTable(i);}}
1327
							else console.log("Failed to Account_Browse");
1328
						});
1329
					}
1330
				} else {
1331
					console.log("Failed to enter");
1332
					btn.disabled = false;
1333
					document.getElementById("txt_skey").style.background = "#466";
1334
					txtSkey.focus();
1335
				}
1336
			});
1337
		} else {
1338
			console.log("Invalid format for key");
1339
			btn.disabled = false;
1340
			document.getElementById("txt_skey").style.background = "#466";
1341
			txtSkey.focus();
1342
		}
1343
	});
1344
};
1345
1346
});
1347